package main

import (
	"fmt"
	"math"
	"reflect"
)

func main() {
	/*
		1.变量：
			Go语言中的变量是分两部分的：
			（1）类型信息：预先定义好的元信息
			（2）值信息：程序运行过程中可动态变化的。

		2.反射：
			（1）反射是指在程序运行期对程序本身进行访问和修改的能力
			（2）程序在编译时，变量被转换为内存地址，变量名不会被编译器写入到可执行部分。在运行程序时，程序无法获取自身的信息。
			（3）支持反射的语言可以在程序编译器将变量的反射信息，如字段名称、类型信息、结构体信息等整合到可执行文件中，
				并给程序提供接口访问反射信息，这样就可以在程序运行期获取类型的反射信息，并且有能力修改它们

		3.reflect包 简介：
			reflect包 提供了 reflect.TypeOf 和 reflect.ValueOf 两个函数来获取对象的 Value 和 Type

		4.TypeOf
			（1）reflect.TypeOf() 函数可以获得任意值的 类型对象（reflect.Type）
			（2）type name 和 type kind
				名称（Name）指 type 关键字构造很多自定义类型
				种类（Kind）指 底层的类型
			Go语言的反射中像数组、切片、Map、指针等类型的变量，它们的 .Name() 都是返回空

		5.ValueOf
			（1）reflect.ValueOf() 函数返回的是 reflect.Value 类型，其中包含了原始值的值信息。
				reflect.Value 与 原始值 之间可以互相转换。

			（2）interface() interface{} 将值以 interface{} 类型返回，可以通过类型断言转换为指定类型
					Int() int64			将值以 int 类型返回，所有有符号整型均可以此方式返回
					Uint() uint64		将值以 uint 类型返回，所有无符号整型均可以以此方式返回
					Float() float64		将值以双精度（float64）类型返回，所有浮点数（float32、float64）均可以以此方式返回
					Bool() bool			将值以 bool 类型返回
					Bytes() []bytes		将值以字节数组 []bytes 类型返回
					String() string		将值以字符串类型返回

			（3）通过反射设置变量的值
			想要在函数中通过反射修改变量的值，需要注意函数参数传递的是值拷贝，必须传递变量地址才能修改变量值。
			而反射中使用专有的 Elem() 方法类来获取指针对应的值。
				Elem() 获取指针对应的值

			（4）IsNil() 和 IsValid()
				1、IsNil()
					func (v Value) IsNil() bool
					报告 v 持有的值是否为 nil。
					v 持有的值的分类必须是 通道、函数、接口、映射、指针、切片 之一；否则 IsNil 函数会导致 panic

				2、IsValid()
					func (v Value) IsValid() bool
					返回 v 是否持有一个值。
					如果 v 是 Value 零值 会返回 false，此时 v 除了 IsValid、String、Kind 之外的方法都会导致 panic

			IsNil() 常用于判断 指针是否为空; IsValid() 常用于判定 返回值是否有效

		6.结构体反射：
		任意值通过 reflect.TypeOf() 获得反射对象信息后，如果它的类型是结构体，
		可以通过反射值对象（reflect.Type）的 NumField() 和 Field() 方法获得结构体成员的详细信息。

					Field(i int) StructField							根据索引，返回索引对应的结构体字段的信息
						NumField() int									返回结构体成员字段数量
			FieldByName(name string) (StructField, bool)				根据给定字符串返回字符串对应的结构体字段的信息
				FieldByIndex(index []int) StructField					多层成员访问时，根据 []int 提供的每个结构体的字段索引，返回字段的信息
		FieldByNameFunc(match func(string) bool) (StructFiled, bool)	根据传入的 匹配函数 匹配需要的字段
						NumMethod() int									返回该类型的方法集中的方法数目
						Method(int) Method								返回该类型方法集中的第 i 个方法
					MethodByName(string)(Method, bool)					根据方法名返回该类型方法集中的方法


	*/
	fmt.Println("--------------------------TypeOf--------------------------")
	var a float32 = 3.14
	v := reflect.TypeOf(a)
	fmt.Println(v) //float32

	fmt.Println("-----------------type name & type kind-------------------")
	pi := math.Pi
	t1 := reflect.TypeOf(pi)
	fmt.Println(t1.Name(), t1.Kind())

	m1 := map[string]interface{}{
		"name": "喜羊羊",
	}
	t2 := reflect.TypeOf(m1)
	fmt.Println("t2.Name():", t2.Name(), "t2.Kind()", t2.Kind()) //t2.Name():  t2.Kind() map
	//t2.Name() 输出是空

	fmt.Println("--------------------------ValueOf---------------------------------")
	var a1 float32 = 3.14
	var b1 int64 = 100
	reflectValue(a1)
	reflectValue(b1)

	//将 int 类型的原始值转换为 reflect.Value 类型
	c := reflect.ValueOf(10)
	fmt.Printf("type c : %T\n", c) //type c : reflect.Value
	/*结果:
	type is float32, value is 3.14
	type is int64, value is 100
	type c : reflect.Value
	*/

	fmt.Println("-----------------------------通过反射设置变量的值 Elem()--------------------------------")

	var a0 int64 = 100
	//reflectSetValue1(a0) //panic: reflect: reflect.Value.SetInt using unaddressable value
	reflectSetValue2(&a0)
	fmt.Println(a0)
	/*结果：
	200
	*/

	fmt.Println("-----------------------IsNil & IsValid------------------------")
	var x *int //*int 类型空指针
	fmt.Println("var x *int IsNil:", reflect.ValueOf(x).IsNil())
	fmt.Println("nil IsValid:", reflect.ValueOf(nil).IsValid())

	y := map[string]int{}
	//尝试从 map 中查找一个不存在的键
	fmt.Println("map中不存在的键：", reflect.ValueOf(y).MapIndex(reflect.ValueOf("美羊羊")).IsValid())
	/*结果：
	var x *int IsNil: true
	nil IsValid: false
	map中不存在的键： false
	*/

	fmt.Println("----------------------------------结构体反射-----------------------------------")
	stu1 := student{Name: "喜羊羊", Score: 100}
	t := reflect.TypeOf(stu1)
	fmt.Println(t.Name(), t.Kind()) //student struct

	//通过 for 循环遍历结构体的所有字段信息
	for i := 0; i < t.NumField(); i++ {
		field := t.Field(i)
		fmt.Println("name:", field.Name, " index:", field.Index, " type:", field.Type, " json tag:", field.Tag.Get("json"))
	}
	/*
		name: Name index: [0] type: string json tag: name
		name: Score index: [1] type: int json tag: score
	*/

	//通过字段名获取指定结构体字段信息
	if scoreField, ok := t.FieldByName("Score"); ok {
		fmt.Println("name:", scoreField.Name, " Index:", scoreField.Index, " type:", scoreField.Type, " json tag:", scoreField.Tag.Get("json"))
	} //name: Score Index: [1] type: int json tag: score

}

// ValueOf
func reflectValue(x interface{}) {
	v := reflect.ValueOf(x)
	k := v.Kind()
	switch k {
	case reflect.Int64:
		//v.Int()从反射中获取整型的原始值，然后通过 int64() 强制类型转换
		fmt.Println("type is int64, value is", int64(v.Int()))
	case reflect.Float32:
		//v.Float() 从反射中获取浮点数类型的原始值，然后通过 float32() 强制类型转换
		fmt.Println("type is float32, value is", float32(v.Float()))
	case reflect.Float64:
		//v.Float() 从反射中获取浮点数类型的原始值，然后通过 float64() 强制类型转换
		fmt.Println("type is float64, value is", float64(v.Float()))
	}
}

// Elem()
func reflectSetValue1(x interface{}) {
	v := reflect.ValueOf(x)
	if v.Kind() == reflect.Int64 {
		v.SetInt(200) //修改的是副本，reflect 包会引发 panic
	}
}

func reflectSetValue2(x interface{}) {
	v := reflect.ValueOf(x)
	if v.Elem().Kind() == reflect.Int64 {
		v.Elem().SetInt(200)
	}
}

// 结构体反射
type student struct {
	Name  string `json:"name"`
	Score int    `json:"score"`
}
